package com.sl.biz.modular.ai.covert;

import cn.hutool.core.util.TypeUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.core.util.DefaultIndenter;
import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.github.victools.jsonschema.generator.Option;
import com.github.victools.jsonschema.generator.SchemaGenerator;
import com.github.victools.jsonschema.generator.SchemaGeneratorConfig;
import com.github.victools.jsonschema.generator.SchemaGeneratorConfigBuilder;
import com.github.victools.jsonschema.module.swagger2.Swagger2Module;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.converter.BeanOutputConverter;
import org.springframework.core.ParameterizedTypeReference;

import java.lang.reflect.Type;
import java.util.Objects;

import static com.github.victools.jsonschema.generator.OptionPreset.PLAIN_JSON;
import static com.github.victools.jsonschema.generator.SchemaVersion.DRAFT_2020_12;

@Slf4j
public class CustomerBeanOutputConverter<T> extends BeanOutputConverter<T> {
    @SuppressWarnings({ "FieldMayBeFinal" })
    private TypeReference<T> typeRef;

    /** The object mapper used for deserialization and other JSON operations. */
    @SuppressWarnings("FieldMayBeFinal")
    private ObjectMapper objectMapper;

    private String jsonSchema;

    public CustomerBeanOutputConverter(Class clazz) {
        this(ParameterizedTypeReference.forType(clazz));
    }
    /**
     * Constructor to initialize with the target class type reference.
     * @param typeRef The target class type reference.
     */
    public CustomerBeanOutputConverter(ParameterizedTypeReference<T> typeRef) {

        this(new CustomizedTypeReference<>(typeRef), null);
    }


    private static class CustomizedTypeReference<T> extends TypeReference<T> {

        private final Type type;

        CustomizedTypeReference(ParameterizedTypeReference<T> typeRef) {
            this.type = typeRef.getType();
        }

        @Override
        public Type getType() {
            return this.type;
        }

    }
    private CustomerBeanOutputConverter(TypeReference<T> typeRef, ObjectMapper objectMapper) {
        super((Class<T>) TypeUtil.getClass(typeRef.getType()));
        Objects.requireNonNull(typeRef, "Type reference cannot be null;");
        this.typeRef = typeRef;
        this.objectMapper = objectMapper != null ? objectMapper : getObjectMapper();
        generateSchema();
    }

    /**
     * Generates the JSON schema for the target type.
     */
    private void generateSchema() {
        Swagger2Module jacksonModule = new Swagger2Module();
        SchemaGeneratorConfigBuilder configBuilder = new SchemaGeneratorConfigBuilder(DRAFT_2020_12, PLAIN_JSON)
                .with(jacksonModule)
                .with(Option.FORBIDDEN_ADDITIONAL_PROPERTIES_BY_DEFAULT);
        SchemaGeneratorConfig config = configBuilder.build();
        SchemaGenerator generator = new SchemaGenerator(config);
        JsonNode jsonNode = generator.generateSchema(typeRef.getType());
        ObjectWriter objectWriter = new ObjectMapper().writer(new DefaultPrettyPrinter()
                .withObjectIndenter(new DefaultIndenter().withLinefeed(System.lineSeparator())));
        try {
            jsonSchema = objectWriter.writeValueAsString(jsonNode);
        }
        catch (JsonProcessingException e) {
            log.error("Could not pretty print json schema for jsonNode: " + jsonNode);
            throw new RuntimeException("Could not pretty print json schema for " + this.typeRef, e);
        }
    }

    @Override
    public String getFormat() {
        String template = "Your response should be in JSON format.\nDo not include any explanations, only provide a RFC8259 compliant JSON response following this format without deviation.\nDo not include markdown code blocks in your response.\nRemove the ```json markdown from the output.\nHere is the JSON Schema instance your output must adhere to:\n```%s```\n";
        return String.format(template,jsonSchema );
    }

}
